/**
 * 
 */
package playGround.border;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import playGround.square.Square;

import be.kuleuven.cs.som.annotate.*;

/**
 * @author Niels Claeys, Maarten Christiaen
 * @version 2.2
 */
public abstract class Border {
	
	/**
	 * A List of Squares that keeps record of the Squares this Border seperates.
	 */
	private List<Square> seperatedSquares;
	
	/**
	 * This method initializes a border.
	 * @param isSlippery 
	 * 			a boolean that says whether or not the border is slippery
	 * @post ...
	 * 			|new.getSlippery() == isSlippery
	 */
	public Border(boolean isSlippery) {
		seperatedSquares = new ArrayList<Square>(2);
		this.isSlippery = isSlippery;
	}
	
	/**
	 * this method add a square to the border
	 * @param square the square to which the border is attached
	 * @throws IllegalArgumentException when the square is not a valid one
	 * 			| ! canHaveAsSquare(square)
	 * @throws NullPointerException
	 * 			| square==null
	 * @post the border is now part of a square.
	 * 			| new.getSeperatedSquares(getNbSquares()+1)== square
	 * @post the number of squares in seperatedSquares is incremented with 1
	 * 			| new.getNbOfSquares() = getNbOfSquares() +1
	 * @note
	 * 		deze methode mag enkel opgeroepen worden via square
	 */
	public void addSquare(Square square) throws IllegalArgumentException,NullPointerException{
		if(square == null)
			throw new NullPointerException();
		if(!canHaveAsSquare(square)) 
			throw new IllegalArgumentException();
		this.seperatedSquares.add(square);
	}

	/**
	 * the method returns a list of squares that are part of the border
	 * @return the list of squares with each element the same
	 * 			| for each index in 0..result.size()
	 *			| result.get(index) == getSquareAt(index)
	 */
	@Basic
	public List<Square> getSeperatedSquares()
	{
		return new ArrayList<Square>(this.seperatedSquares);
	}
	/**
	 * This method can get a square on a specific index
	 * @param index the index of the selected square
	 * @return the square on that index
	 *		| result==this.seperatedSquares.get(index)
	 * @throws indexOutOfBoundsException when the index is to large or to low
	 *		| index<0 || index >1
	 */
	public Square getSquareAt(int index) throws IndexOutOfBoundsException
	{
		if(index<0 || index >1)
			throw new IndexOutOfBoundsException();
		return this.seperatedSquares.get(index);
	}
	/**
	 *
	 * This method returns the number of squares
	 * @return the number of squares
	 *		| result == this.getSeperatedSquares().size()
	 */
	public int getNbSquares()
	{
		return this.seperatedSquares.size();
	}
	/**
	 * this method can remove a square from the list.
	 * @param square the square to be removed
	 * @post the square is removed from the list
	 *		| !(new.containsSquare(square)
	 * @post the number of squares in seperatedSquares is decremented with 1
	 *		| new.getNbOfSquares() = getNbOfSquares() -1
	 */
	public void removeSquare(Square square)
	{
		if(square == null) 
			throw new IllegalArgumentException();
		this.seperatedSquares.remove(square);
		if(this.getNbSquares()==1)
			square.removeBorder(this);
	}
	/**
	 * this method checks whether or not the border can have this square.
	 * @param square the square to be checked
	 * @return whether or not this border can have the square
	 *		| if ( ! this.getNbSquares()<2) 
	 *		| then result == false
	 *		| else if(square == null)
	 *		|then result == false
	 *		|else if( getSeperatedSquares().contains(square))
	 *		| then result == false
	 *
	 */
	public boolean canHaveAsSquare(Square square)
	{
		boolean test = square.hasBorder(this);
		return ((this.getNbSquares()<2) && (!square.isTerminated()) && (!seperatedSquares.contains(square)) && test);
	}
	/**
	 * this method checks whether or not the border has valid squares
	 * @return whether or not the squares are valid
	 *		| for each square in 0..getNbSquares()
	 *		| 	if(!square.hasBorder(this)||square.isTerminated())
	 *		|		result == false
	 *		| if(this.getNbSquares()>2)
	 *		|	result == false
	 *		| else
	 *		|	result == true
	 */
	public boolean hasProperSquares()
	{
		if (this.getNbSquares()>2)
			return false;
		for(Square square: this.seperatedSquares)
			if(!square.hasBorder(this)||square.isTerminated())
				return false;
		return true;
	}
	/**
	 * this method checks whether or not the list contains this square
	 * @param square the square you want to search in the list
	 * @return true if the list contains this square
	 *		| result == this.getSeperatedSquares().contains(square)
	 */
	public boolean containsSquare(Square square)
	{
		return this.seperatedSquares.contains(square);
	}
	/**
	 * this method returns whether or not the border is slippery
	 * @return true if the border is slippery
	 */
	public boolean getSlippery(){
		
		return this.isSlippery;
	}
	/**
	 * Variable registering the sliperiness of this Border.
	 */
	private final boolean isSlippery;

	/**
	 * Variable registering the state of this border (terminated or not).
	 */
	private boolean isTerminated;
	/**
	 * Method to set the state of this border to terminated.
	 * @post the border is terminated
	 * 			| new.isTerminated == true
	 * @post the list of squares is cleared
	 * 			| new.getSeperatedSquares.isEmpty()
	 * @post this border is removed from all the squares
	 * 			| for each square in this.getSeperatedSquares()
	 * 			| !(new.containsSquare(square))
	 */
	public void terminate() {
		if(this.isTerminated==false) {
			this.isTerminated = true;
			for(Iterator <Square> it= seperatedSquares.iterator();it.hasNext();){
				Square square = it.next();
				it.remove();
				square.removeBorder(this);
			}
		}
	}
	
	/**
	 * The method that checks whether or not this border is terminated
	 * @return whether or not this border is terminated.
	 */
	public boolean isTerminated() {
		return this.isTerminated;
	}
	/**
	 * this method checks whether or not this border can be on the floor
	 */
	public abstract boolean canBeOnFloor();
	/**
	 * This method checks whether or not this border can be opened or closed
	 */
	public abstract boolean canBeOpenedOrClosed();
	/**
	 * This method checks whether or not you can pass through this border.
	 */
	public abstract boolean canPassThrough();
	/**
	 * This method opens or closes the border.
	 */
	public void openOrClose(){
	}

}
